﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection.Emit;
using System.Reflection;
using ZXW.DotNet.Common.Basic.Helpers;

namespace ZXW.DotNet.Common.Basic.Emit.DynamicEntity
{
    /// <summary>
    ///     动态创建实体对象
    /// </summary>
    /// <remarks>
    ///     
    /// </remarks>
    public class DynamicEntityEmitter
    {
        #region 字段

        private bool saveFile = false;
        private string assemblyName = "DynamicEntityAssembly";
        private string theFileName;
        private AssemblyBuilder dynamicAssembly;
        protected ModuleBuilder moduleBuilder;
        /// <summary>
        /// DynamicTypeName -- DynamicType
        /// </summary>
        private IDictionary<string, Type> proxyTypeDictionary = new Dictionary<string, Type>();

        #endregion

        #region 构造函数

        /// <summary>
        ///     构造函数
        /// </summary>
        public DynamicEntityEmitter()
            : this(false)
        {

        }

        public DynamicEntityEmitter(bool save)
        {
            this.saveFile = save;
            this.theFileName = this.assemblyName + ".dll";

            AssemblyBuilderAccess assemblyBuilderAccess =
                this.saveFile ? AssemblyBuilderAccess.RunAndSave : AssemblyBuilderAccess.Run;
            this.dynamicAssembly =
                AppDomain.CurrentDomain.DefineDynamicAssembly(
                new AssemblyName(this.assemblyName),
                assemblyBuilderAccess);

            if (this.saveFile)
            {
                this.moduleBuilder = dynamicAssembly.DefineDynamicModule(
                    "MainModule",
                    this.theFileName);
            }
            else
            {
                this.moduleBuilder = dynamicAssembly.DefineDynamicModule("MainModule");
            }
        }
        #endregion

        #region 编辑动态函数

        public Type EmitProxyType(string entityTypeName, IDictionary<string, Type> entityFieldDic)
        {
            string typeName = string.Format("{0}.{1}", this.assemblyName, entityTypeName);
            TypeBuilder typeBuilder = this.moduleBuilder.DefineType(
                typeName, TypeAttributes.Public | TypeAttributes.Class | TypeAttributes.Serializable);

            List<FieldBuilder> fieldBuilderList = new List<FieldBuilder>();

            foreach (string propertyName in entityFieldDic.Keys)
            {
                #region 定义属性

                Type propertyType = entityFieldDic[propertyName];

                FieldBuilder fieldBuilder = typeBuilder.DefineField(
                   string.Format("m_{0}", propertyName), propertyType, FieldAttributes.Private);

                fieldBuilderList.Add(fieldBuilder);

                fieldBuilder.SetConstant(TypeHelper.ChangeType(propertyType,
                    TypeHelper.GetDefaultValue(propertyType)));

                PropertyBuilder propertyBuilder = typeBuilder.DefineProperty(
                    propertyName, PropertyAttributes.None, propertyType, null);

                #region 定义属性的get方法
                MethodBuilder getPropertyBuilder = typeBuilder.DefineMethod(
            "get",
            MethodAttributes.Public |
            MethodAttributes.SpecialName |
            MethodAttributes.HideBySig,
            propertyType,
            Type.EmptyTypes);

                ILGenerator getAIL = getPropertyBuilder.GetILGenerator();
                getAIL.Emit(OpCodes.Ldarg_0);
                getAIL.Emit(OpCodes.Ldfld, fieldBuilder);
                getAIL.Emit(OpCodes.Ret);
                #endregion

                #region 定义属性的set方法
                MethodBuilder setPropertyABuilder = typeBuilder.DefineMethod(
            "set",
            MethodAttributes.Public |
            MethodAttributes.SpecialName |
            MethodAttributes.HideBySig,
            null,
            new Type[] { propertyType });

                ILGenerator setAIL = setPropertyABuilder.GetILGenerator();
                setAIL.Emit(OpCodes.Ldarg_0);
                setAIL.Emit(OpCodes.Ldarg_1);
                setAIL.Emit(OpCodes.Stfld, fieldBuilder);
                setAIL.Emit(OpCodes.Ret);
                #endregion

                propertyBuilder.SetGetMethod(getPropertyBuilder);
                propertyBuilder.SetSetMethod(setPropertyABuilder);

                #endregion
            }

            #region 向动态程序集添加构造函数

            ConstructorBuilder ctor = typeBuilder.DefineConstructor(
                MethodAttributes.Public,
                CallingConventions.Standard,
                Type.EmptyTypes);

            ILGenerator ctorGen = ctor.GetILGenerator();
            ctorGen.Emit(OpCodes.Ldarg_0);
            ctorGen.Emit(OpCodes.Call, typeof(object).GetConstructor(new Type[] { }));

            foreach (FieldBuilder fieldBuilder in fieldBuilderList)
            {
                if (fieldBuilder.FieldType == typeof(string))
                {
                    ctorGen.Emit(OpCodes.Ldarg_0);
                    ctorGen.Emit(OpCodes.Ldstr, "");
                    ctorGen.Emit(OpCodes.Stfld, fieldBuilder);
                }
            }

            ctorGen.Emit(OpCodes.Ret);

            #endregion

            Type target = typeBuilder.CreateType();

            return target;
        }

        #endregion

        #region 保存
        /// <summary>
        /// 将动态程序集保存到磁盘
        /// </summary>
        public void Save()
        {
            if (this.saveFile)
            {
                this.dynamicAssembly.Save(theFileName);
            }
        }
        #endregion

    }
}
